#pragma once
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <unordered_map>

static bool PairCompare(std::pair<int, int>&a, const int& b)
{
	return a.first < b;
};

//! LessSort Used
static bool LessSortElement(std::pair<int, int>&a, std::pair<int, int>&b)
{
	return a.first < b.first;
};

struct VectorHash
{
	size_t operator()(const std::vector<int> &vec) const
	{
		std::hash<int> hasher;
		size_t result = 0;
		for (auto x : vec) {
			result ^= hasher(x);
		}
		return result;
	};
};

struct B31Element
{
	std::string type = "B31";
	int elementId;
	int nodeId_1;
	int nodeId_2;
	int addition = 0;
	std::string elementSet;
};

class ReadINP
{
public:
	ReadINP();
	~ReadINP();

	bool readInpMesh(std::string inpPath);

public:
	static ReadINP* GetInstance();

	//! "ST"/"TH"/"ED"
	static std::string AnalysisType;

	//! true为"3d"，false为"2d"
	static bool AnalysisDimention3D;

	static std::vector<int>& GetNodeId()
	{
		return ReadINP::GetInstance()->allNodeId;
	};

	static std::vector<double>& GetNodeCoor()
	{
		return ReadINP::GetInstance()->allNodeCoor;
	};

	static int GetNodeCount()
	{
		return ReadINP::GetInstance()->allNodeCoor.size() / 3;
	};

	static int GetCellCount()
	{
		return ReadINP::GetInstance()->allElementType.size();
	};

	static std::vector<int>& GetCellId()
	{
		return ReadINP::GetInstance()->allElementId;
	};

	static std::vector<int>& GetCellNode()
	{
		return ReadINP::GetInstance()->allElementNode;
	};

	static std::vector<int>& GetCellType()
	{
		return ReadINP::GetInstance()->allElementType;
	};

	static std::vector<int>& GetCellIndex()
	{
		return ReadINP::GetInstance()->elementIndex;
	};

	static std::vector<std::string>& GetCellFormulation()
	{
		return ReadINP::GetInstance()->elementFormulation;
	};




	static std::vector<std::string>& GetNodeSetName()
	{
		return ReadINP::GetInstance()->allNodeSetName;
	};

	static std::vector<std::vector<int>>& GetNodeSet()
	{
		return ReadINP::GetInstance()->nodeSetData;
	};

	static std::vector<std::string>& GetCellSetName()
	{
		return ReadINP::GetInstance()->allElementSetName;
	};

	static std::vector<std::vector<int>>& GetCellSet()
	{
		return ReadINP::GetInstance()->elementSetData;
	};

	static std::map<std::string,std::vector<std::string>> & GetnodeElementSurfaceSetName()
	{
		return ReadINP::GetInstance()->nodeElementSurfaceSetName;
	}

	static std::map<std::string, std::vector<std::string>>& GetduplicateSetName()
	{
		return ReadINP::GetInstance()->duplicateSetName;
	}

	std::vector<std::string> keyWordsUnsupport;
	//read inp文件过程中产生的warning，数据存放。
	std::vector<std::string> warning;

public:
	std::string ElementSetToNodeSet(std::string setName);

	int GetSetType(std::string setName);

	std::string GetElementType(int elementIdIBE);

	std::string GetSetElementType(const std::string setName);

	std::vector<int> GetSet(int type, std::string setName);

	std::vector<int> GetBusingSet(std::string setName);

	std::vector<int> GetGapElementSet(std::string setName);

	std::vector<int> GetDCOUP3DElementSet(std::string setName);


	void AddSet(std::vector<int>& idVec, int type, std::string setName, bool ifInpId = false);

	//! 只针对于直接使用的inp的值
	void AddSet(std::vector<std::string>& idVec, int type, std::string setName);

	std::string GetSetOrAddSet(int type, std::string setName);

	int GetIBENodeIDFromINPID(const int& inpID);

	std::vector<double> GetNodeCoorFromID(const int& inpID, bool ifInpId = true);

	int GetIBEElementIDFromINPID(const int& inpID);

	std::vector<int> GetInterSet(int type, std::string setName);

	std::vector<int> GetOutterSet(int type, std::string setName);

	bool IfHaveSet(int type,std::string& setName);

	void changeElementFormulation(std::string& setName,std::string targetFormulation);

	void addElementOrNode(int type, std::string& setName, int id);

	void AddAllElementSet(std::string setName);

	std::string internalSetToOutSet(int type, std::string& setName);

	std::string sectionSetToUnique(std::string& sourceSetName);

	int write_gmsh_file(std::string exe_path, std::string read_file_path, std::string write_file_path);
	int write_vtk_file(std::string exe_path, std::string read_file_path, std::string write_file_path);
	int write_gmsh_file_from_vtk(std::string exe_path, std::string read_file_path, std::string write_file_path);
public:
	std::map<std::pair<int, std::string>, std::vector<int>> setInterMap;

	std::vector<std::string> nodeInternalSetName;
	std::vector<std::vector<int>> nodeInternalSetData;//! all node set internal data

	std::vector<std::string> elementInternalSetName;
	std::vector<std::vector<int>> elementInternalSetData;//! all element set internal data


	//! output
	std::vector<std::vector<int>> partIndex; //! number of the node, element,nodeset,elementset,and surface in different part

	std::vector<int> allNodeId;                  //! all node index
	std::vector<double> allNodeCoor;              //! all node coordinate

	std::vector<int> allElementId;               //! all element id
	std::vector<int> allElementNode;             //! all node element contains
	std::vector<int> allElementType;             //! element type
	std::vector<std::string> elementFormulation; //! element formulation name
	std::vector<int> elementIndex;               //! element index in allElementNode


	std::vector<std::string> allNodeSetName;
	std::vector<std::vector<int>> nodeSetData;//! all node set data
	//将inp的节点编号放入到容器中
	std::vector<std::vector<int>> nodeSetDataInp;
	std::map<std::string, std::vector<std::vector<int>>> nodeSetNameAndDataInp;

	std::vector<std::string> allElementSetName;
	std::vector<std::vector<int>> elementSetData;//! all element set data

	//因为bushing是特殊的连接；不能将其转换成ibe的单元。因此将bushing单元的set名和bushing单元的id号，存在两个容器中。通过这个单元的id号，可以检索到节点号。
	std::vector<std::string> bushingElementSetName;
	std::vector<std::vector<int>> bushingElementSetData;

	//DCOUP3D是一个单元对应一个节点；不能将其转换成ibe的单元；因此将DCOUP3D单元的set名和DCOUP3D单元的id号，存在两个容器中。通过这个单元的id号，可以检索到节点号。
	std::vector<std::string> dcoup3dElementSetName;
	std::vector<std::vector<int>> dcoup3dElementSetData;

	//因为GAP是特殊的接触；不能将其转换成ibe的单元。因此将GAP单元的set名和GAP单元的id号，存在两个容器中。通过这个单元的id号，可以检索到节点号。
	std::vector<std::string> gapElementSetName;
	std::vector<std::vector<int>> gapElementSetData;
	//! output end

	std::vector<std::string> allElementTypeName; //! element type

	std::map<std::string,std::vector<std::string>> nodeElementSurfaceSetName;//将三类SetName，按类别存储到map中。key是node/element/surface;value是存储各自set名称的容器。
	std::map<std::string, std::vector<std::string>> duplicateSetName;

	//存储bushing单元的信息
	std::map<int, std::vector<int>> bushingMap;

	//存储gap单元的信息
	std::map<int, std::vector<int>> gapMap;

	std::string errorMsg;

	int endElement = 1;

	//在判定哪些是单元的自由表面时，创建存储face node的容器
	std::vector<std::vector<int>> ibeElementNodeOfFaceAll;
private:
	void ParseSingleNodeBlock(std::vector<std::string>& block);

	void ParseSingleElementBlock(std::vector<std::string>& block);

	void ParseSingleSetBlock(std::vector<std::string>& block);

	void ParseSpecialElementBlock(std::vector<std::string>& block);

	//新增GAP代码
	//处理element中 TYPE类型为GAPUNI、GAPUNIT、GAPCYL类型的单元数据。
	void ParseSpecialElementBlock_GAP(std::vector<std::string>& block);
	//处理element中 TYPE类型为GAPUNI、GAPUNIT、GAPCYL类型的单元数据。不讲gap单元转换成IBE的单元，只获取其节点。
	void ParseSpecialElementBlock_GAP_NotEle(std::vector<std::string>& block);

	void ParseCONN3D2Block(std::vector<std::string>& block);

	void ParseDCOUP3DBlock(std::vector<std::string>& block);

	void ParseSingleDCOUP3DBlock(std::vector<std::string>& block);

	void ParseSPRING1Block(std::vector<std::string>& block);

	void ParseSPRING2Block(std::vector<std::string>& block);

	void ParseB31Block(std::vector<std::string>& block);

	void ParseSingleInstance(std::vector<std::string>& block);

	std::vector<std::string> partName;
	int FindPartNum(std::string& tempNmae);

	void split(std::vector<std::string>& result, const std::string& line, const char* sep);

	void split(std::vector<const char*>& result, const std::string& line, const char* sep);

	//新增函数：对比两个vector容器是否重复
	bool areEquivalent(const std::vector<int> vec1, const std::vector<int> vec2);
	//新增函数：将转换后所有ibe单元的节点map顺序存放到 容器中。
	void GetIbeElementNodeOfFaceAll();

private:
	struct SYSTEMCoor
	{//! size == 3 
		std::vector<double> a;  //! original node
		std::vector<double> x;  //! local x direction unit vector
		std::vector<double> y;  //! local y direction unit vector
		std::vector<double> z;  //! local z direction unit vector
	};

	SYSTEMCoor* systemMeshUsed;

	void ParseNodeSystem(std::vector<std::string>& block);

private:
	static ReadINP* mesh;

	int cellIndex;

	int nodeId;

	int cellId;

	std::map<std::string, std::pair<int, int>> elementNodeMap; //! abaqus element type  ==>  (node count  ==>  simdroid element type)

	std::unordered_map<std::string, std::string> elementFormulationMap;

	std::map<std::string, int> faceMaps; //! face name(S1 etc.)  ==>  face index in element

	std::map<int, std::string> abaqus_type_to_meshio_type;

	std::vector <std::string> highOrder2DElement;

	std::map<std::string, int> instanceToPart;

	std::map<std::string, int> nodeSetNameMap;
	std::map<std::string, int> elementSetNameMap;
	std::map<std::string, int> surfaceSetNameMap;

	int partNumberID;
	//! <part,inpID> => ibeID
	std::map<std::pair<int, int>, int> nodeIdInpToIBEMap;

	int GetIBENodeIDFromINPID(const int& partNum, const int& inpID);

	//! <part,inpID> => ibeID
	std::map<std::pair<int, int>, int> elementIdInpToIBEMap;

	int GetIBEElementIDFromINPID(const int& partNum, const int& inpID);

	private:
		bool readOnce;

		std::vector<std::vector<std::string>> unhandleSurface;

		bool isInPartKey;

		bool isInAssmbly;

	private:
		double translation[3];
		double rotation[9];

		int componentId;	
};
